iT邦幫忙

2022 iThome 鐵人賽

DAY 5
0

我們來建立幾個重複的整數:10 和 257,比對一下他們的ID看看:

var_1 = 10
var_2 = 10
id(var_1), id(var_2)
(4338205200, 4338205200)
var_3 = 257
var_4 = 257
id(var_3), id(var_4)
(4575421584, 4575427280)

怪事發生了!為什麼同樣指向 10 的 var_1 與 var_2 兩個 id 相同,var_3 與 var_4 卻是完全兩個不同的 id 呢?

interning 機制

Python(CPython)啟動時,為了效率更好,會把 -5~256 整數加入到一個 global list 中(類似快取),所以每次我們參考這範圍的整數,就會參考到這些預先產生的物件。

這些整數稱為 singletons,Python認為這些小整數被使用到的機率較大,於是預先準備好。

延續昨天的資源回收話題,當時我們拿了list[1,2,3]來測試,卻沒有用簡單的整數 10 來測試,為什麼呢?即是因為 10 這個整數在Python啟動時已經被預先產生並被參考,因此印出的參考值會與你想像的不同。

整數 257 超過了上述範圍,因此每次我們指派一個整數變數"257",Python都會產生一個新的整數 257 物件。

string 的 interning

說完整數來說字串————部分的字串也有interning,比如以下:

  • 變數名稱
  • function名稱
  • class名稱

Python 用什麼規則抓出以上名稱?字串必須以底線或字母開頭,且只能包含底線、字母和數字

像 hello_world 這種符合以上條件的 string literals 也會被 interning。

為什麼有 string interning?都是為了速度和記憶體的最佳化。寫程式很常用到字典,在字典裡找東西就要用到 string 的比對。假設我們要比對以下兩個字串是否相同:

a = "this_is_hello_world"
b = "this_is_hello_world"

要比對 a,b 兩個字串是否相同(a == b),通常只能一個字母一個字母比較。但如果我們知道這個字串已經 interned,只要比對 a,b 是否指向同一個記憶體位置就好了!

比較記憶體位置的語法是 a is b

手動 interning string

不是所有字串都會被自動 interned,但我們可以手動做這件事。

import time, sys

# 字串夠長才看得出比對時間差異
a = 'I love to go out at Saturday night.' * 100000
b = 'I love to go out at Saturday night.' * 100000

start = time.perf_counter()
for i in range(10000):    # 多次比對才看得出時間差異
    a == b
end = time.perf_counter()
print("compare with characters:", end - start)

# 字串夠長才看得出比對時間差異
a = sys.intern('I love to go out at Saturday night.' * 100000)
b = sys.intern('I love to go out at Saturday night.' * 100000)

start = time.perf_counter()
for i in range(10000):    # 多次比對才看得出時間差異
    a is b
end = time.perf_counter()
print("compare with ids:", end - start)
compare with characters: 0.8398300419939915
compare with ids: 0.000305499997921288

可以看到比對的時間差距很大!這在比對大量字串時可能會很好用。不過一般來說,你不需要自己去做 string interning。

我們明天見~

參考:Python 3: Deep Dive (Part 1 - Functional)


上一篇
Python 裡的資源回收
下一篇
Python內建的提升效能機制(二)
系列文
小青蛇變大蟒蛇——進階Python學起來!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言